---
title: Notifications
description: A library that provides an API to fetch push notification tokens and to present, schedule, receive and respond to notifications.
sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-50/packages/expo-notifications'
packageName: 'expo-notifications'
iconUrl: '/static/images/packages/expo-notifications.png'
---

import APISection from '~/components/plugins/APISection';
import { APIInstallSection } from '~/components/plugins/InstallSection';
import PlatformsSection from '~/components/plugins/PlatformsSection';
import { AndroidPermissions } from '~/components/plugins/permissions';
import { Collapsible } from '~/ui/components/Collapsible';
import { ConfigReactNative, ConfigPluginProperties } from '~/ui/components/ConfigSection';
import { ContentSpotlight } from '~/ui/components/ContentSpotlight';
import { SnackInline } from '~/ui/components/Snippet';
import { Tab, Tabs } from '~/ui/components/Tabs';
import { PlatformTags } from '~/ui/components/Tag';

`expo-notifications` provides an API to fetch push notification tokens and to present, schedule, receive and respond to notifications.

<PlatformsSection title="Push notifications Platform Compatibility" android ios />

<PlatformsSection
  title="Local notifications Platform Compatibility"
  android
  emulator
  ios
  simulator
/>

## Features

- Schedule a one-off notification for a specific date or some time from now
- Schedule a notification repeating in some time interval (or a calendar date match on iOS)
- Get and set the application badge icon number
- Fetch a native device push token so you can send push notifications with FCM and APNs
- Fetch an Expo push token so you can send push notifications with Expo
- Listen to incoming notifications in the foreground and background
- Listen to interactions with notifications
- Handle notifications when the app is in the foreground
- Imperatively dismiss notifications from Notification Center/tray
- Create, update, and delete Android notification channels
- Set custom icon and color for notifications on Android

## Installation

<APIInstallSection />

## Usage

Check out the example Snack below to see Notifications in action, make sure to use a physical device to test it. Push notifications don't work on emulators/simulators.

<SnackInline label='Push Notifications' dependencies={['expo-device', 'expo-permissions', 'expo-notifications']}>

```jsx
import { useState, useEffect, useRef } from 'react';
import { Text, View, Button, Platform } from 'react-native';
import * as Device from 'expo-device';
import * as Notifications from 'expo-notifications';

Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldShowAlert: true,
    shouldPlaySound: false,
    shouldSetBadge: false,
  }),
});

export default function App() {
  const [expoPushToken, setExpoPushToken] = useState('');
  const [notification, setNotification] = useState(false);
  const notificationListener = useRef();
  const responseListener = useRef();

  useEffect(() => {
    registerForPushNotificationsAsync().then(token => setExpoPushToken(token));

    notificationListener.current = Notifications.addNotificationReceivedListener(notification => {
      setNotification(notification);
    });

    responseListener.current = Notifications.addNotificationResponseReceivedListener(response => {
      console.log(response);
    });

    return () => {
      Notifications.removeNotificationSubscription(notificationListener.current);
      Notifications.removeNotificationSubscription(responseListener.current);
    };
  }, []);

  return (
    <View
      style={{
        flex: 1,
        alignItems: 'center',
        justifyContent: 'space-around',
      }}>
      <Text>Your expo push token: {expoPushToken}</Text>
      <View style={{ alignItems: 'center', justifyContent: 'center' }}>
        <Text>Title: {notification && notification.request.content.title} </Text>
        <Text>Body: {notification && notification.request.content.body}</Text>
        <Text>Data: {notification && JSON.stringify(notification.request.content.data)}</Text>
      </View>
      <Button
        title="Press to schedule a notification"
        onPress={async () => {
          await schedulePushNotification();
        }}
      />
    </View>
  );
}

async function schedulePushNotification() {
  await Notifications.scheduleNotificationAsync({
    content: {
      title: "You've got mail! 📬",
      body: 'Here is the notification body',
      data: { data: 'goes here' },
    },
    trigger: { seconds: 2 },
  });
}

async function registerForPushNotificationsAsync() {
  let token;

  if (Platform.OS === 'android') {
    await Notifications.setNotificationChannelAsync('default', {
      name: 'default',
      importance: Notifications.AndroidImportance.MAX,
      vibrationPattern: [0, 250, 250, 250],
      lightColor: '#FF231F7C',
    });
  }

  if (Device.isDevice) {
    const { status: existingStatus } = await Notifications.getPermissionsAsync();
    let finalStatus = existingStatus;
    if (existingStatus !== 'granted') {
      const { status } = await Notifications.requestPermissionsAsync();
      finalStatus = status;
    }
    if (finalStatus !== 'granted') {
      alert('Failed to get push token for push notification!');
      return;
    }
    // Learn more about projectId:
    // https://docs.expo.dev/push-notifications/push-notifications-setup/#configure-projectid
    token = (await Notifications.getExpoPushTokenAsync({ projectId: 'your-project-id' })).data;
    console.log(token);
  } else {
    alert('Must use physical device for Push Notifications');
  }

  return token;
}
```

</SnackInline>

### Present the notification to the user

```ts
import * as Notifications from 'expo-notifications';

// First, set the handler that will cause the notification
// to show the alert

Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldShowAlert: true,
    shouldPlaySound: false,
    shouldSetBadge: false,
  }),
});

// Second, call the method

Notifications.scheduleNotificationAsync({
  content: {
    title: 'Look at that notification',
    body: "I'm so proud of myself!",
  },
  trigger: null,
});
```

### Handle push notifications with navigation

If you'd like to deep link to a specific screen in your app when you receive a push notification, you can configure either of Expo's navigation systems to do that.

<Tabs>

<Tab label="Expo Router">

You can use Expo Router's [built-in deep linking](/router/create-pages) to handle incoming URLs from push notifications. Simply configure the root layout to listen for incoming and initial notification events.

```tsx app/_layout.tsx
import { useEffect } from 'react';
import * as Notifications from 'expo-notifications';
import { router } from 'expo-router';

function useNotificationObserver() {
  useEffect(() => {
    let isMounted = true;

    function redirect(notification: Notifications.Notification) {
      const url = notification.request.content.data?.url;
      if (url) {
        /* @info Push the URL. You may want to verify the format before navigating. */
        router.push(url);
        /* @end */
      }
    }

    /* @info Handle the initial push notification. */
    Notifications.getLastNotificationResponseAsync() /* @end */
      .then(response => {
        if (!isMounted || !response?.notification) {
          return;
        }
        redirect(response?.notification);
      });

    /* @info Listen for runtime notifications. */
    const subscription = Notifications.addNotificationResponseReceivedListener(response => {
      /* @end */
      redirect(response.notification);
    });

    return () => {
      isMounted = false;
      subscription.remove();
    };
  }, []);
}

export default function Layout() {
  /* @info Observe at the root. Ensure this layout never returns <b>null</b> or the navigation will go unhandled. */
  useNotificationObserver();
  /* @end */

  return <Slot />;
}
```

</Tab>

<Tab label="React Navigation">

React Navigation's manual [linking configuration](https://reactnavigation.org/docs/navigation-container#linking) can be configured to handle incoming redirects from push notifications:

```tsx App.tsx
import React from 'react';
import { Linking } from 'react-native';
import * as Notifications from 'expo-notifications';
import { NavigationContainer } from '@react-navigation/native';

export default function App() {
  return (
    <NavigationContainer
      linking={{
        config: {
          // Configuration for linking
        },
        async getInitialURL() {
          // First, you may want to do the default deep link handling
          // Check if app was opened from a deep link
          const url = await Linking.getInitialURL();

          if (url != null) {
            return url;
          }

          // Handle URL from expo push notifications
          const response = await Notifications.getLastNotificationResponseAsync();

          return response?.notification.request.content.data.url;
        },
        subscribe(listener) {
          const onReceiveURL = ({ url }: { url: string }) => listener(url);

          // Listen to incoming links from deep linking
          const eventListenerSubscription = Linking.addEventListener('url', onReceiveURL);

          // Listen to expo push notifications
          const subscription = Notifications.addNotificationResponseReceivedListener(response => {
            const url = response.notification.request.content.data.url;

            // Any custom logic to see whether the URL needs to be handled
            //...

            // Let React Navigation handle the URL
            listener(url);
          });

          return () => {
            // Clean up the event listeners
            eventListenerSubscription.remove();
            subscription.remove();
          };
        },
      }}>
      {/* Your app content */}
    </NavigationContainer>
  );
}
```

See more details on [React Navigation documentation](https://reactnavigation.org/docs/deep-linking/#third-party-integrations).

</Tab>

</Tabs>

## Configuration

### Credentials

#### Android

Firebase Cloud Messaging credentials are required for all Android apps to receive push notifications in your app (except when testing in Expo Go). For more information, see how to get [FCM credentials](/push-notifications/push-notifications-setup/#android) for your app.

#### iOS

To register your iOS device and automatically enable push notifications for your EAS Build, see [push notification setup](/push-notifications/push-notifications-setup/#ios).

### App config

To configure `expo-notifications`, use the built-in [config plugin](/config-plugins/introduction/) in the app config (**app.json** or **app.config.js**) for [EAS Build](/build/introduction) or with `npx expo run:[android|ios]`. The plugin allows you to configure the following properties that cannot be set at runtime and require building a new app binary to take effect:

<ConfigPluginProperties
  properties={[
    {
      name: 'icon',
      platform: 'android',
      description:
        'Local path to an image to use as the icon for push notifications. 96x96 all-white png with transparency.',
    },
    {
      name: 'color',
      default: '#ffffff',
      platform: 'android',
      description:
        'Tint color for the push notification image when it appears in the notification tray.',
    },
    {
      name: 'sounds',
      description:
        'Array of local paths to sound files (.wav recommended) that can be used as custom notification sounds.',
    },
  ]}
/>

Here is an example of using the config plugin in the app config file:

```json app.json
{
  "expo": {
    "plugins": [
      [
        "expo-notifications",
        {
          "icon": "./local/assets/notification-icon.png",
          "color": "#ffffff",
          "sounds": [
            "./local/assets/notification-sound.wav",
            "./local/assets/notification-sound-other.wav"
          ]
        }
      ]
    ]
  }
}
```

<ConfigReactNative>

Learn how to configure the native projects in the [installation instructions in the `expo-notifications` repository](https://github.com/expo/expo/tree/main/packages/expo-notifications#installation-in-bare-react-native-projects).

</ConfigReactNative>

> The iOS APNs entitlement is _always_ set to 'development'. Xcode automatically changes this to 'production' during the archive.
> [Learn more](https://stackoverflow.com/a/42293632/4047926).

## Permissions

### Android

- On Android, this module requires permission to subscribe to the device boot. It's used to setup scheduled notifications when the device (re)starts.
  The `RECEIVE_BOOT_COMPLETED` permission is added automatically through the library **AndroidManifest.xml**.

- Starting from Android 12 (API level 31), to schedule the notification that triggers at the exact time, you need to add
  `<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>` to **AndroidManifest.xml**.
  You can read more about the [exact alarm permission](https://developer.android.com/about/versions/12/behavior-changes-12#exact-alarm-permission).

- On Android 13, app users must opt-in to receive notifications via a permissions prompt automatically triggered by the operating system.
  This prompt will not appear until at least one notification channel is created. The `setNotificationChannelAsync` must be called before
  `getDevicePushTokenAsync` or `getExpoPushTokenAsync` to obtain a push token. You can read more about the new notification permission behavior for Android 13
  in the [official documentation](https://developer.android.com/develop/ui/views/notifications/notification-permission#new-apps).

<AndroidPermissions permissions={['RECEIVE_BOOT_COMPLETED', 'SCHEDULE_EXACT_ALARM']} />

### iOS

No usage description is required, see [notification-related permissions](#fetching-information-about-notifications-related-permissions).

## Notification events listeners

Notification events include incoming notifications, interactions your users perform with notifications (this can be tapping on a notification,
or interacting with it via [notification categories](#managing-notification-categories-interactive-notifications)), and rare occasions when your notifications may be dropped.

A few different listeners are exposed, so we've provided a chart below which will hopefully help you understand when you can expect each one to be triggered:

| User interacted with notification? |        App state         | Listener(s) triggered                                                   |
| :--------------------------------- | :----------------------: | ----------------------------------------------------------------------- |
| false                              |        Foreground        | `NotificationReceivedListener`                                          |
| false                              | Foreground or Background | `BackgroundNotificationTask`                                            |
| false                              |          Killed          | none                                                                    |
| true                               |        Foreground        | `NotificationReceivedListener` & `NotificationResponseReceivedListener` |
| true                               |        Background        | `NotificationResponseReceivedListener`                                  |
| true                               |          Killed          | `NotificationResponseReceivedListener`                                  |

In the table above, whenever `NotificationResponseReceivedListener` is triggered, the same would apply to the `useLastNotificationResponse` hook.

### Background notifications

> **warning** Background event listeners are not supported in Expo Go.

A background notification (iOS) or a data-only notification (Android) is a remote notification that does not display an alert, play a sound, or add a badge to your app's icon. The purpose of a background notification is to provide a way to wake up your app to trigger an app data refresh in the background.

To handle notifications while the app is in the background on iOS, add `remote-notification` as a value to the array under `ios.infoPlist.UIBackgroundModes` key in your [app config](/versions/latest/config/app/), and add `"content-available": 1` to your push notification payload. Under normal circumstances, the "content-available" flag should launch your app if it isn't running
and wasn't killed by the user. However, this is ultimately decided by the OS, so it might not always happen.

On Android, data-only notifications are sent using the `data` key of the push notification request payload.

## Additional information

### Set custom notification sounds

Custom notification sounds are only supported when using [EAS Build](/build/introduction).

To add custom push notification sounds to your app, add the `expo-notifications` plugin to your **app.json** file and then under the `sounds` key, provide an array of local paths to sound files that can be used as custom notification sounds. These local paths are local to your project.

```json app.json
{
  "expo": {
    "plugins": [
      [
        "expo-notifications",
        {
          "sounds": ["local/path/to/mySoundFile.wav"]
        }
      ]
    ]
  }
}
```

After building your app, the array of files will be available for use in both [`NotificationContentInput`](#notificationcontentinput) and [`NotificationChannelInput`](#notificationchannelinput).
You only need to provide the base filename. Here's an example using the config above:

```js
await Notifications.setNotificationChannelAsync('new-emails', {
  name: 'E-mail notifications',
  sound: 'mySoundFile.wav', // Provide ONLY the base filename
});

await Notifications.scheduleNotificationAsync({
  content: {
    title: "You've got mail! 📬",
    sound: 'mySoundFile.wav', // Provide ONLY the base filename
  },
  trigger: {
    seconds: 2,
    channelId: 'new-emails',
  },
});
```

You can also manually add notification files to your Android and iOS projects if you prefer:

<Collapsible summary="Manually adding notification sounds on Android">

On Androids 8.0+, playing a custom sound for a notification requires more than setting the `sound` property on the `NotificationContentInput`.
You will also need to configure the `NotificationChannel` with the appropriate `sound`, and use it when sending/scheduling the notification.

For the example below to work, you would place your **email-sound.wav** file in **android/app/src/main/res/raw/**.

```ts
// Prepare the notification channel
await Notifications.setNotificationChannelAsync('new-emails', {
  name: 'E-mail notifications',
  importance: Notifications.AndroidImportance.HIGH,
  sound: 'email-sound.wav', // <- for Android 8.0+, see channelId property below
});

// Eg. schedule the notification
await Notifications.scheduleNotificationAsync({
  content: {
    title: "You've got mail! 📬",
    body: 'Open the notification to read them all',
    sound: 'email-sound.wav', // <- for Android below 8.0
  },
  trigger: {
    seconds: 2,
    channelId: 'new-emails', // <- for Android 8.0+, see definition above
  },
});
```

</Collapsible>

<Collapsible summary="Manually adding notification sounds on iOS">

On iOS, all that's needed is to place your sound file in your Xcode project (see the screenshot below),
and then specify the sound file in your `NotificationContentInput`, like this:

```ts
await Notifications.scheduleNotificationAsync({
  content: {
    title: "You've got mail! 📬",
    body: 'Open the notification to read them all',
    sound: 'notification.wav',
  },
  trigger: {
    // ...
  },
});
```

<ContentSpotlight
  alt="notification.wav inside of app resources in Xcode project organizer"
  src="/static/images/notification-sound-ios.jpeg"
  className="max-w-[305px]"
/>

</Collapsible>

### Android push notification payload specification

When sending a push notification, put an object conforming to the following type as `data` of the notification:

```ts
export interface FirebaseData {
  title?: string;
  message?: string;
  subtitle?: string;
  sound?: boolean | string;
  vibrate?: boolean | number[];
  priority?: AndroidNotificationPriority;
  badge?: number;
}
```

### Interpret the iOS permissions response

On iOS, permissions for sending notifications are a little more granular than they are on Android.
Because of this, you should rely on the `NotificationPermissionsStatus`'s `ios.status` field, instead of the root `status` field.
This value will be one of the following, accessible under `Notifications.IosAuthorizationStatus`:

- `NOT_DETERMINED`: The user hasn't yet made a choice about whether the app is allowed to schedule notifications
- `DENIED`: The app isn't authorized to schedule or receive notifications
- `AUTHORIZED`: The app is authorized to schedule or receive notifications
- `PROVISIONAL`: The application is provisionally authorized to post noninterruptive user notifications
- `EPHEMERAL`: The app is authorized to schedule or receive notifications for a limited amount of time

### Manage notification categories (interactive notifications)

Notification categories allow you to create interactive push notifications, so that a user can respond directly to the incoming notification
either via buttons or a text response. A category defines the set of actions a user can take, and then those actions are applied to a notification
by specifying the `categoryIdentifier` in the [`NotificationContent`](#notificationcontent).

<ContentSpotlight
  src="/static/images/sdk/notifications/categories.png"
  className="max-w-[800px]"
  alt="Image of notification categories on Android and iOS"
/>

On iOS, notification categories also allow you to customize your notifications further. With each category, not only can you set interactive actions a user can take, but you can also configure things like the placeholder text to display when the user disables notification previews for your app.

> Notification categories are not supported on the web and all related methods will result in noop.

## Platform specific guides

### Handling notification channels&ensp;<PlatformTags platforms={['android 8+']} />

Starting in Android 8.0 (API level 26), all notifications must be assigned to a channel. For each channel,
you can set the visual and auditory behavior that is applied to all notifications in that channel.
Then, users can change these settings and decide which notification channels from your app should be intrusive or visible at all,
as [Android developer docs](https://developer.android.com/training/notify-user/channels) states.

If you do not specify a notification channel, `expo-notifications` will create a fallback channel for you, named **Miscellaneous**.
We encourage you to always ensure appropriate channels with informative names are set up for the application and to always send notifications to these channels.

> Calling these methods is a no-op for platforms that do not support this feature (Android below version 8.0 (26), iOS and web).

### Custom notification icon and colors&ensp;<PlatformTags platforms={['android']} />

You can configure the [`notification.icon`](../config/app.mdx#notification) and [`notification.color`](../config/app.mdx#notification) keys
in the project's **app.json** if you are using [Expo Prebuild](/workflow/prebuild) or by using the [`expo-notifications` config plugin directly](#expo-config).
These are build-time settings, so you'll need to recompile your native Android app with `eas build -p android` or `npx expo run:android` to see the changes.

For your notification icon, make sure you follow [Google's design guidelines](https://material.io/design/iconography/product-icons.html#design-principles)
(the icon must be all white with a transparent background) or else it may not be displayed as intended.

You can also set a custom notification color **per-notification** directly in your [`NotificationContentInput`](#notificationcontentinput) under the `color` attribute.

## API

```js
import * as Notifications from 'expo-notifications';
```

<APISection
  packageName="expo-notifications"
  apiName="Notifications"
  headersMapping={{
    fetch: 'Fetch tokens for push notifications',
    listen: 'Listen to notification events',
    inForeground: 'Handle incoming notifications when the app is in the foreground',
    inBackground: 'Handle incoming notifications when the app is not in the foreground',
    permissions: 'Fetch information about notifications-related permissions',
    badge: 'Manage application badge icon',
    schedule: 'Schedule notifications',
    dismiss: 'Dismiss notifications',
    channels: 'Manage notification channels (Android-specific)',
    categories: 'Manage notification categories (interactive notifications)',
  }}
/>
